package com.fj.netty.chat.client;

import com.fj.netty.chat.message.*;
import com.fj.netty.chat.protocol.MessageCodec;
import com.fj.netty.chat.protocol.MessageLengthFieldDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 客户端
 * @author fj
 * @date 2024/1/11 14:58
 */
@Slf4j
public class ChatClient {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        AtomicBoolean LOGIN = new AtomicBoolean(false);
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new MessageLengthFieldDecoder());
//                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                            ch.pipeline().addLast(new MessageCodec());
                            //空闲连接检测器，超过指定时间会触发对应事件
                            ch.pipeline().addLast(new IdleStateHandler(0,3,0));
                            //自定义事件监听器，处理特定事件
                            ch.pipeline().addLast(new ChannelDuplexHandler(){
                                @Override
                                public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                    if (evt instanceof IdleStateEvent){
                                        IdleStateEvent event = (IdleStateEvent) evt;
                                        if (event.state()== IdleState.WRITER_IDLE) {
//                                            log.debug("3秒内无操作，发送心跳包");
                                            ctx.writeAndFlush(new PingRequestMessage());
                                        }
                                    }
                                }
                            });
                            ch.pipeline().addLast("send handler", new ChannelOutboundHandlerAdapter(){
                                @Override
                                public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                    log.debug("发送消息：{}",msg);
                                    super.write(ctx, msg, promise);
                                }
                            });
                            ch.pipeline().addLast("client handler",new ChannelInboundHandlerAdapter(){
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    log.debug("连接成功！");
                                    new Thread(()->{
                                        Scanner scanner = new Scanner(System.in);
                                        System.out.println("输入用户名：");
                                        String username = scanner.nextLine();
                                        System.out.println("输入密码：");
                                        String password = scanner.nextLine();
                                        LoginRequestMessage loginRequestMessage = new LoginRequestMessage(username, password);
                                        ctx.writeAndFlush(loginRequestMessage);
                                        //阻塞等待服务器响应
                                        try {
                                            countDownLatch.await();
                                        } catch (InterruptedException e) {
                                            throw new RuntimeException(e);
                                        }
                                        if (!LOGIN.get()){
                                            ctx.channel().close();
                                            return;
                                        }
                                        waitOperate(username, ctx, scanner);
                                    }).start();
                                    super.channelActive(ctx);
                                }

                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    log.debug("收到消息：{}", msg);
                                    if (msg instanceof LoginResponseMessage){
                                        LoginResponseMessage message = (LoginResponseMessage) msg;
                                        if (message.isSuccess()){
                                            LOGIN.set(true);
                                        }
                                        countDownLatch.countDown();
                                    }
                                    super.channelRead(ctx, msg);
                                }
                            });
                        }
                    });
            Channel channel = bootstrap.connect("127.0.0.1", 6789).sync().channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            group.shutdownGracefully();
            System.exit(0);
        }
    }

    private static void waitOperate(String username,ChannelHandlerContext ctx, Scanner scanner) {
        while (true){
            System.out.println("==============================");
            System.out.println("send [username] [content]");
            System.out.println("gsend [group name] [content]");
            System.out.println("gcreate [group name] [m1,m2,m3...]");
            System.out.println("gmembers [group name]");
            System.out.println("gjoin [group name]");
            System.out.println("gquit [group name]");
            System.out.println("quit");
            System.out.println("==============================");
            String[] s = scanner.nextLine().split(" ");
            Message message = null;
            switch (s[0]){
                case "send":
                    ChatRequestMessage chatRequestMessage = new ChatRequestMessage();
                    chatRequestMessage.setFrom(username);
                    chatRequestMessage.setTo(s[1]);
                    chatRequestMessage.setContent(s[2]);
                    message = chatRequestMessage;
                    break;
                case "gsend":
                    GroupChatRequestMessage groupChatRequestMessage = new GroupChatRequestMessage();
                    groupChatRequestMessage.setFrom(username);
                    groupChatRequestMessage.setGroupName(s[1]);
                    groupChatRequestMessage.setContent(s[2]);
                    message = groupChatRequestMessage;
                    break;
                case "gcreate":
                    GroupCreateRequestMessage groupCreateRequestMessage = new GroupCreateRequestMessage();
                    groupCreateRequestMessage.setUsername(username);
                    groupCreateRequestMessage.setGroupName(s[1]);
                    if (s.length > 2){
                        HashSet<String> memberSet = new HashSet<>(Arrays.asList(s[2].split(",")));
                        groupCreateRequestMessage.setMembers(memberSet);
                    }
                    message = groupCreateRequestMessage;
                    break;
                case "gmembers":
                    GroupMembersRequestMessage groupMembersRequestMessage = new GroupMembersRequestMessage();
                    groupMembersRequestMessage.setGroupName(s[1]);
                    message = groupMembersRequestMessage;
                    break;
                case "gjoin":
                    GroupJoinRequestMessage groupJoinRequestMessage = new GroupJoinRequestMessage();
                    groupJoinRequestMessage.setUsername(username);
                    groupJoinRequestMessage.setGroupName(s[1]);
                    message = groupJoinRequestMessage;
                    break;
                case "gquit":
                    GroupQuitRequestMessage groupQuitRequestMessage = new GroupQuitRequestMessage();
                    groupQuitRequestMessage.setUsername(username);
                    groupQuitRequestMessage.setGroupName(s[1]);
                    message = groupQuitRequestMessage;
                    break;
                case "quit":
                    ctx.channel().close();
                    break;
                default:
                    break;
            }
            if (message!=null){
                ctx.writeAndFlush(message);
            }
        }
    }
}
